/*
 * Compiler.cpp
 *
 *  Created on: 2016年9月1日
 *      Author: wll
 */

#include "Compiler.h"
#include "WllInterpreter.h"
#include "Wll1Interpreter.h"
#include "Wll2Interpreter.h"
#include "Wll3Interpreter.h"
#include "LR1Parsers.h"
#include "LanguageAlgorithm.h"
#include "WllUtil.h"
#include <fstream>
#include "WllTrace.h"
#include <vector>
#include <cstring>
#include <cassert>
#include <iostream>
#include <CompilerManager.h>
#include "WllSingleton.h"
#include "MatchMode.h"

using namespace std;

Compiler::Compiler()
{
    this->parser_strategy.Set(new LR1Parsers(this));
    this->interpreter = nullptr;
}

Compiler::~Compiler()
{
	LanguageParsers* parser = this->parser_strategy.Get();
	if (parser != nullptr)
	{
		delete parser;
	}
}

bool Compiler::LoadLanguage()
{
    CompilerManager* compiler_manager = Singleton<CompilerManager>::GetInstance();
    string grammar_filename = compiler_manager->GetBoostrapCompilerFileName();
    return this->LoadLanguage(grammar_filename);
}

bool Compiler::LoadLanguage(const string& grammar_file_name)
{
    compiler_id.grammar_file_name = grammar_file_name;
    compiler_id.compiler_id = CaculateCompilerId(grammar_file_name);
    compiler_id.parent_compiler_id = "";

    return ::LoadLanguage(grammar_file_name.c_str(), this->languages);
}

bool Compiler::Process(std::string& input_file_name, std::string& output_file_name)
{
    ifstream input_stream(input_file_name);
    if (!input_stream)
    {
        cerr<<"open file ["<<input_file_name<<"] failed!"<<endl;
        return false;
    }
    ofstream output_stream(output_file_name);
    if (!output_stream)
    {
        cerr<<"open file ["<<output_file_name<<"] failed!"<<endl;
        return false;
    }
    return Process(input_stream, output_stream);
}

bool Compiler::Process(istream& inf, ostream& outf)
{
	Symbols start_symbol = this->languages.GetDefaultStartSymbol();
	assert(!(start_symbol == Symbols::NULL_SYMBOL));
	vector<Symbols> output_symbols;
	StreamSymbolsFlow input_symbols(inf);
    bool retval = this->Process(input_symbols, output_symbols, start_symbol);
    if (retval)
    {
        outf<<output_symbols;
    }

	return retval;
}

bool Compiler::Process(std::vector<Symbols> &input_symbols, std::vector<Symbols> &output_symbols, MatchMode match_mode)
{
    ArraySymbolsFlow arraySymbolsFlow(input_symbols);
    return this->Process(arraySymbolsFlow, output_symbols, this->languages.GetDefaultStartSymbol(), match_mode);
}

bool Compiler::Process(SymbolsFlow &input_symbols, std::vector<Symbols>& output_symbols, Symbols start_symbol, MatchMode match_mode)
{
    do
    {
        LanguageTree* source_tree = NULL;
        LanguageTree* destination_tree = NULL;

        INFO("parse start ...");
        LanguageParsers* parser = this->parser_strategy.Get();
        if(!parser->Parse(input_symbols, source_tree, start_symbol, match_mode))
        {
            DestroyTree(source_tree);
            INFO("Parse failed");
            return false;
        }
        INFO("parse end success");

        INFO("translate start ...");
        if(!TranslateTree(source_tree, destination_tree, this->languages.destination_rules))
        {
            DestroyTree(source_tree);
            DestroyTree(destination_tree);
            INFO("Translate failed");
            return false;
        }
        INFO("translate end success");

        vector<Symbols> translate_output_symbols;
        DisplayTreeLeaves(translate_output_symbols, destination_tree);
        INFO("translate_output_symbols="<<translate_output_symbols);

        INFO("inteprete start ...");
        if (!WllInterpreter::Interpret(translate_output_symbols, output_symbols, this))
        {
            INFO("Intepreter failed");
            DestroyTree(source_tree);
            DestroyTree(destination_tree);
            return false;
        }
    }while (!input_symbols.IsEnd());

	return true;
}


//for debug
void Compiler::DisplayLanguage(ostream& o)
{
	o<<this->languages;
}

void Compiler::DumpLanguage(const std::string& file_name)
{
	ofstream output_stream(file_name);
	DisplayLanguage(output_stream);
}

void Compiler::Save(const string &file_name)
{
    ofstream output_stream(file_name);
    this->compiler_id.Save(output_stream);
    this->languages.Save(output_stream);
    INFO("compiler saved into [" << file_name << "]");

    this->parser_strategy.Get()->Save();
}

Compiler * Compiler::Load(const CompilerId *compiler_id)
{
    Compiler* compiler = nullptr;

    string cache_wll_file_name = CompilerManager::GetCacheWllFileName(compiler_id->compiler_id);
    if (access(cache_wll_file_name.c_str(),R_OK) == 0)
    {
        INFO("[" << cache_wll_file_name << "] disk cache hint");
        INFO("loading [" << cache_wll_file_name << "] from disk");

        ifstream input_stream(cache_wll_file_name);
        compiler = new Compiler();
        compiler->compiler_id.Load(input_stream);
        compiler->languages.Load(input_stream);
        compiler->languages.Initialize();
        compiler->languages.grammar_file_name = compiler_id->grammar_file_name;

        INFO("[" << cache_wll_file_name << "] loaded SUCCESS");

        compiler->parser_strategy.Get()->Load();
    }
    else
    {
        INFO("[" << cache_wll_file_name << "] disk cache NOT hint");
    }

    return compiler;
}
